﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using SharePointListSecurity.Data;
using Microsoft.SharePoint;
using System.Xml.Linq;
using Microsoft.SharePoint.WebControls;
using Microsoft.SharePoint.Utilities;

namespace SharePointListSecurity.Model
{
    public class SharepointListSecurityService
    {
        #region members

        readonly SPListSecurityContextDataContext context;
        readonly SPWeb spWeb;
        SPUser currentUser;
        #endregion

        #region ctor

        public SharepointListSecurityService()
        {
            this.spWeb = SPContext.Current.Web;
            context = new SPListSecurityContextDataContext(spWeb.Site.Url);
            currentUser = spWeb.CurrentUser;
        }

        public SharepointListSecurityService(SPWeb sPweb,SPUser user)
        {
            currentUser = user;
            this.spWeb = sPweb;
            context = new SPListSecurityContextDataContext(spWeb.Site.Url);
        }

        #endregion

        #region public methods
        /// <summary>
        /// this function returns the hidden fields for current user 
        /// of the form saved in permission lists 
        /// its context dependent on the SPControlMode 
        /// Display for ViewForm,Edit for EditForm ,New for NewForm and Invalid
        /// </summary>
        public List<SPField> GetHiddenFields(SPList list, SPControlMode controlMode)
        {
            switch (controlMode)
            {
                case SPControlMode.Display:
                    return GetDisplayFormHiddenFields(list);

                case SPControlMode.Edit:
                    return GetEditFormHiddenFields(list);

                case SPControlMode.New:
                    return GetAddFormHiddenFields(list);
                default:
                    return new List<SPField>();
            }
        }
        /// <summary>
        /// this function returns the readonly fields for the current user 
        /// of the edit form saved in permission lists 
        /// </summary>
        public List<SPField> GetReadOnlyFields(SPList list)
        {
            IEnumerable<EditFormFieldsPermissions> editPermissionsReadOnlyEdit = context.EditFormFieldsPermissions
                                                                      .Where(p => new Guid(p.ListID) == list.ID
                                                                      && (p.DisplayMode == PermissionType.ReadOnly ||
                                                                          p.DisplayMode == PermissionType.Editable)
                                                                      );
            //list of read only fields
            return (from item in editPermissionsReadOnlyEdit
                    let doesUserHavePermissions = IsFieldReadOnly(item.DisplayModeUsersAndGroups, item.DisplayModeUsersAndGroupsId, item.DisplayMode)
                    where doesUserHavePermissions
                    select list.Fields[new Guid(item.ColumnID)]).ToList();
        }
        /// <summary>
        /// method returns true if only default 
        /// view is permitted
        /// </summary>
        public bool IsOnlyDefaultViewPermissed(SPList list)
        {
              var viewPermission = context.ViewPermissions
                                   .FirstOrDefault(p => new Guid(p.ListID) == list.ID);
            if (viewPermission != null)
                 return  bool.Parse(viewPermission.IsAllowed);
            return false;
        }

        #endregion

        #region  Private Methods
        /// <summary>
        /// this function returns the hidden fields of View Form
        /// calculated for the current user
        /// </summary>
        private List<SPField> GetDisplayFormHiddenFields(SPList list)
        {
            IEnumerable<DisplayFormFieldsPermissions> displayPermissions = context.DisplayFormFieldsPermissions
                                                             .Where(p => new Guid(p.ListID) == list.ID);
            return (from item in displayPermissions
                    let calculatedPermission = IsFieldExcluded(item.UsersAndGroups, item.UsersAndGroupsId, item.Permission)
                    where calculatedPermission
                    select list.Fields[new Guid(item.ColumnID)]).ToList();
        }
        /// <summary>
        /// this function returns the hidden fields of Edit Form
        /// calculated for the current user
        /// </summary>
        private List<SPField> GetEditFormHiddenFields(SPList currentList)
        {
            IEnumerable<EditFormFieldsPermissions> editPermissionsShowHide = context.EditFormFieldsPermissions
                                                                         .Where(p => new Guid(p.ListID) == currentList.ID
                                                                         && (p.Permission == PermissionType.HideField ||
                                                                             p.Permission == PermissionType.ShowField)
                                                                         );
            //list of hidden fields
            return (from item in editPermissionsShowHide
                    let calculatedPermission = IsFieldExcluded(item.UsersAndGroups, item.UsersAndGroupsId, item.Permission)
                    where calculatedPermission
                    select currentList.Fields[new Guid(item.ColumnID)]).ToList();
        }
        /// <summary>
        /// this function returns the hidden fields of New Form
        /// calculated for the current user
        /// </summary>
        private List<SPField> GetAddFormHiddenFields(SPList list)
        {
            //get all new form items settings
            IEnumerable<AddFormFieldsPermissions> addPermissions = context.AddFormFieldsPermissions
                                                                    .Where(p => new Guid(p.ListID) == list.ID);
            //list of hidden fields on New form
            return (from item in addPermissions
                    let calculatedPermission = IsFieldExcluded(item.UsersAndGroups, item.UsersAndGroupsId, item.Permission)
                    where calculatedPermission
                    select list.Fields[new Guid(item.ColumnID)]).ToList();
        }
        /// <summary>
        /// returns true if the field's permession for the current user is set to hide
        /// </summary>
        private bool IsFieldExcluded(IList<string> usersAndGroups, IList<int?> usersAndGroupsIds, string permissionType)
        {
            UsersAndGroupsManager usersAndGroupsManager = new UsersAndGroupsManager(spWeb);

            var isExcluded = permissionType == PermissionType.ShowField;
            int index = 0;
            foreach (var userOrGroup in usersAndGroupsIds)
            {
                //current item group or user id
                int id = Convert.ToInt32(userOrGroup);
                //current item group or user name
                string name = usersAndGroups.ElementAt(index);

                if (id == currentUser.ID)
                {
                    return !isExcluded;
                }

                else
                {

                    SPGroup group;
                    bool isSharepointGroup = usersAndGroupsManager.IsSharepointGroup(id, out group);
                    if (isSharepointGroup)
                    {
                        if (usersAndGroupsManager.IsMember(group, currentUser))
                        {
                            return !isExcluded;
                        }
                    }

                    else
                    {

                        SPPrincipalInfo[] activeDirectoryUsers;
                        SPPrincipalInfo userInfo;
                        bool isADGroup = usersAndGroupsManager.IsADGroup(name, out activeDirectoryUsers, out userInfo);
                        if (isADGroup)
                        {
                            foreach (var usr in activeDirectoryUsers)
                            {
                                if (usr.LoginName == currentUser.LoginName)
                                {

                                    return !isExcluded;
                                }
                            }
                        }

                    }

                }
                index++;
            }
            return isExcluded;
        }
        /// <summary>
        /// returns true if the field's permession for the current user is set to read only
        /// </summary>
        private bool IsFieldReadOnly(IList<string> usersAndGroups, IList<int?> usersAndGroupsIds, string permissionType)
        {
            bool isReadOnly = permissionType == PermissionType.Editable;
            UsersAndGroupsManager usersAndGroupsManager = new UsersAndGroupsManager(spWeb);


            int index = 0;
            foreach (var userOrGroup in usersAndGroupsIds)
            {
                //current item group or user id
                int id = Convert.ToInt32(userOrGroup);
                //current item group or user name
                string name = usersAndGroups.ElementAt(index);

                if (id == currentUser.ID)
                {
                    return !isReadOnly;
                }

                else
                {

                    SPGroup group;
                    bool isSharepointGroup = usersAndGroupsManager.IsSharepointGroup(id, out group);
                    if (isSharepointGroup)
                    {
                        if (usersAndGroupsManager.IsMember(group, currentUser))
                        {
                            return !isReadOnly;
                        }
                    }

                    else
                    {

                        SPPrincipalInfo[] activeDirectoryUsers;
                        SPPrincipalInfo userInfo;
                        bool isADGroup = usersAndGroupsManager.IsADGroup(name, out activeDirectoryUsers, out userInfo);
                        if (isADGroup)
                        {
                            foreach (var usr in activeDirectoryUsers)
                            {
                                if (usr.LoginName == currentUser.LoginName)
                                {

                                    return !isReadOnly;
                                }
                            }
                        }

                    }

                }
                index++;
            }
            return isReadOnly;
        }
        #endregion
    }
}